﻿using HtmlAgilityPack;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Windows.Data.Html;
using Windows.System;
using Windows.UI;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Documents;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows8Accelerator.Core.Xaml.Caching;
using Windows8Accelerator.Core.Xaml.Controls;
using Windows8Accelerator.Core.Xaml.Converters;
using Windows8Accelerator.Core.Xaml.Html;

namespace Windows8Accelerator.Core.Xaml.Helpers
{
    public class HtmlHelper : HtmlHelperBase
    {
        public override async Task PreLoadHtmlImagesAsync(string html)
        {
            html = WebUtility.HtmlDecode(html);

            var htmlDocument = new HtmlDocument();
            htmlDocument.LoadHtml(html);

            VisitPreLoadHtmlImages(htmlDocument.DocumentNode);
        }

        private static void VisitPreLoadHtmlImages(HtmlNode htmlNode)
        {
            switch (htmlNode.NodeType)
            {
                case HtmlNodeType.Document:
                case HtmlNodeType.Element:
                    var tag = htmlNode.Name.ToLower();
                    if (tag.Equals("img") && htmlNode.Attributes.Contains("src"))
                    {
                        var imageSrc = htmlNode.Attributes["src"].Value;
                        try
                        {
                            CacheManager.GetOrRetrieveUri(imageSrc);
                        }
                        catch { }
                    }
                    else
                    {
                        foreach (var item in htmlNode.ChildNodes)
                        {
                            VisitPreLoadHtmlImages(item);
                        }
                    }
                    break;
            }
        }

        public static async Task<List<HtmlImage>> GetImagesFromWebPageAsync(Uri uri)
        {
            var result = new List<HtmlImage>();
            var htmlWeb = new HtmlWeb();
            var htmlDocument = await htmlWeb.LoadFromWebAsync(uri, Encoding.UTF8, new NetworkCredential());
            VisitGetHtmlImages(htmlDocument.DocumentNode, result, uri);
            return result;
        }

        private static void VisitGetHtmlImages(HtmlNode htmlNode, List<HtmlImage> result, Uri baseUri)
        {
            switch (htmlNode.NodeType)
            {
                case HtmlNodeType.Document:
                case HtmlNodeType.Element:
                    var tag = htmlNode.Name.ToLower();
                    if (tag.Equals("img") && htmlNode.Attributes.Contains("src"))
                    {
                        var htmlImage = new HtmlImage()
                        {
                            ImageSource = AdjustUrl(htmlNode.Attributes["src"].Value, baseUri)
                        };
                        if (htmlNode.Attributes.Contains("alt"))
                            htmlImage.AlternateText = htmlNode.Attributes["alt"].Value;
                        result.Add(htmlImage);
                    }
                    else
                    {
                        foreach (var item in htmlNode.ChildNodes)
                        {
                            VisitGetHtmlImages(item, result, baseUri);
                        }
                    }
                    break;
            }
        }      

        public static List<HtmlImage> GetImagesFromHtml(string html, Uri baseUri)
        {
            html = WebUtility.HtmlDecode(html);
            var result = new List<HtmlImage>();
            var htmlDocument = new HtmlDocument();
            htmlDocument.LoadHtml(html);
            VisitGetHtmlImages(htmlDocument.DocumentNode, result, baseUri);
            return result;
        }

        public static async Task<List<HtmlPage>> GetPagesFromWebPageAsync(Uri uri, bool loadContent)
        {
            var result = new List<HtmlPage>();
            var htmlWeb = new HtmlWeb();
            var htmlDocument = await htmlWeb.LoadFromWebAsync(uri, Encoding.UTF8, new NetworkCredential());
            await VisitGetHtmlPagesAsync(htmlDocument.DocumentNode, result, uri, loadContent);
            return result;
        }

        private static string[] ExcludePageExtension = new[] { ".jpg", ".png", ".gif", ".jpeg", ".svg" };

        private async static Task VisitGetHtmlPagesAsync(HtmlNode htmlNode, List<HtmlPage> result, Uri baseUri, bool loadContent, bool externalPages = false)
        {
            switch (htmlNode.NodeType)
            {
                case HtmlNodeType.Document:
                case HtmlNodeType.Element:
                    var tag = htmlNode.Name.ToLower();
                    if (tag.Equals("a") && htmlNode.Attributes.Contains("href"))
                    {
                        var pageSource = AdjustUrl(htmlNode.Attributes["href"].Value, baseUri);
                        var pageSource1 = pageSource.ToLower();

                        if (ExcludePageExtension.Any((i) => pageSource1.EndsWith(i)))
                            return;

                        if (!externalPages && !(new Uri(pageSource)).Host.Equals(baseUri.Host))
                            return;

                        if ((new Uri(pageSource)).Equals(baseUri))
                            return;

                        if (result.Any((r) => object.Equals(r.PageSource, pageSource)))
                            return;

                        try
                        {
                            var htmlPage = new HtmlPage()
                            {
                                PageSource = pageSource,
                                Title = WebUtility.HtmlDecode(htmlNode.InnerText),
                            };
                            if (loadContent)
                                await LoadHtmlPageAsync(htmlPage);
                            result.Add(htmlPage);
                        }
                        catch { }
                    }
                    else
                    {
                        foreach (var item in htmlNode.ChildNodes)
                        {
                            await VisitGetHtmlPagesAsync(item, result, baseUri, loadContent);
                        }
                    }
                    break;
            }
        }

        public static async Task<HtmlPage> LoadHtmlPageAsync(HtmlPage htmlPage)
        {
            try
            {
                var uri = new Uri(htmlPage.PageSource);
                var htmlWeb = new HtmlWeb();
                var htmlDocument = await htmlWeb.LoadFromWebAsync(uri, Encoding.UTF8, new NetworkCredential());
                try
                {
                    htmlPage.Content = htmlDocument.DocumentNode.InnerHtml;
                }
                catch { }
                try
                {
                    string title = htmlPage.Title;
                    VisitGetHtmlTitleAsync(htmlDocument.DocumentNode, ref title);
                    htmlPage.Title = WebUtility.HtmlDecode(title);
                }
                catch { }
                try
                {
                    if (htmlPage.Images == null)
                        htmlPage.Images = new List<HtmlImage>();
                    VisitGetHtmlImages(htmlDocument.DocumentNode, htmlPage.Images, uri);
                }
                catch { }
            }
            catch { }
            return htmlPage;
        }

        private static void VisitGetHtmlTitleAsync(HtmlNode htmlNode, ref string title)
        {
            switch (htmlNode.NodeType)
            {
                case HtmlNodeType.Document:
                case HtmlNodeType.Element:
                    var tag = htmlNode.Name.ToLower();
                    if (tag.Equals("title"))
                    {
                        title = htmlNode.InnerText;
                        return;
                    }
                    else
                    {
                        foreach (var item in htmlNode.ChildNodes)
                        {
                            VisitGetHtmlTitleAsync(item, ref title);
                        }
                    }
                    break;
            }
        }

        public async static Task<List<HtmlPage>> GetPagesFromWebPageAsync(string html, Uri baseUri, bool loadContent)
        {
            html = WebUtility.HtmlDecode(html);
            var result = new List<HtmlPage>();
            var htmlDocument = new HtmlDocument();
            htmlDocument.LoadHtml(html);
            await VisitGetHtmlPagesAsync(htmlDocument.DocumentNode, result, baseUri, loadContent);
            return result;
        }

        public override string ConvertToText(string html)
        {
            if (string.IsNullOrEmpty(html))
                return html;
            return HtmlUtilities.ConvertToText(html).Trim();
        }
    }

    public class ParagraphHtmlSource : DependencyObject, INotifyPropertyChanged
    {
        public string HtmlSource
        {
            get { return (string)GetValue(HtmlSourceProperty); }
            set { SetValue(HtmlSourceProperty, value); }
        }
        public static readonly DependencyProperty HtmlSourceProperty =
            DependencyProperty.Register("HtmlSource", typeof(string), typeof(ParagraphHtmlSource), new PropertyMetadata("", PropertyChangedCallback));

        public RichTextBlock RichTextBlock
        {
            get { return (RichTextBlock)GetValue(RichTextBlockProperty); }
            set { SetValue(RichTextBlockProperty, value); }
        }
        public static readonly DependencyProperty RichTextBlockProperty =
            DependencyProperty.Register("RichTextBlock", typeof(RichTextBlock), typeof(ParagraphHtmlSource), new PropertyMetadata(null, PropertyChangedCallback));

        private static void PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var propertyName = "";
            if (e.Property == HtmlSourceProperty)
                propertyName = "HtmlSource";
            else if (e.Property == RichTextBlockProperty)
                propertyName = "RichTextBlock";
            ((ParagraphHtmlSource)d).OnPropertyChanged(propertyName);
        }

        private void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }
}
